<?php

namespace app\service\cloud;

use Easemob\Agora\ServiceRtc;
use Easemob\Auth;
use Easemob\Message;
use Easemob\Push;
use Easemob\User;

/**
 * 环信 IM composer require maniac/easemob-php
 */
class EasemobService
{

    protected mixed $appKey;
    protected mixed $auth;
    protected mixed $user;
    protected mixed $push;
    protected mixed $message;
    protected mixed $agora_auth;
    protected mixed $client_secret;
    protected mixed $client_id;

    /**
     * @throws \Exception
     */
    public function __construct()
    {
        $this->appKey = config("cloud.easemob.appKey");
        $this->client_id = config("cloud.easemob.client_id");
        $this->client_secret = config("cloud.easemob.client_secret");
        $api_uri = config("cloud.easemob.api_uri");
        try {
            $this->auth = new Auth($this->appKey, $this->client_id, $this->client_secret);
            // 设置 REST 域名，沙箱环境使用，不是沙箱环境会自动获取
            if ($api_uri) {
                $this->auth->setApiUri($api_uri);
            }
            // 初始化授权对象，声网 token 初始化
            $agora = config("cloud.agora");
            $this->agora_auth = new Auth($agora['app_key'], $agora['app_id'], $agora['app_certificate'], true, $agora['expire_time']);
            $this->agora_auth->setApiUri($api_uri);
            $this->user = new User($this->auth);
            $this->push = new Push($this->auth);
            $this->message = new Message($this->auth);
        } catch (\Exception $e) {
            throw new \Exception("系统错误");
        }
    }


//==================================== 使用环信 App Token/User Token 鉴权 ======================================================================

    /**
     * 生成 Easemob userToken
     * @param string $username 环信IM用户名，或者uuid
     * @param string $password 环信用户密码，传递时获取 Easemob userToken 否则获取 Agora userToken
     * @return mixed
     */
    public function getUserToken(string $username, string $password): mixed
    {
        $userToken = $this->auth->getUserToken($username, $password);
        return $userToken['access_token'];
    }

    /**
     * 生成仅含 AgoraChat 权限的 Agora userToken
     * @param string $username 环信IM用户名，或者uuid
     * @param string|null $password 环信用户密码，传递时获取 Easemob userToken 否则获取 Agora userToken
     * @return mixed
     */
    public function getAgoraUserToken(string $username, string $password = null): mixed
    {
        $userToken = $this->agora_auth->getUserToken($username, $password);
        return $userToken['access_token'];
    }

    /**
     * 生成包含 AgoraChat 权限和 AgoraRTC (JOIN_CHANNEL) 权限的 Agora userToken
     * @param string $channelName
     * @param string $uid
     * @param string $username 环信IM用户名，或者uuid
     * @param string|null $password 环信用户密码，传递时获取 Easemob userToken 否则获取 Agora userToken
     * @param int $expire 过期时间，单位：s
     * @return mixed
     */
    public function getAgoraRTCUserToken(string $channelName, string $uid, string $username, string $password = null, int $expire = 600): mixed
    {
        // grant rtc privileges
        $serviceRtc = new ServiceRtc($channelName, $uid);
        $serviceRtc->addPrivilege($serviceRtc::PRIVILEGE_JOIN_CHANNEL, $expire);
        $configuration = array(
            $serviceRtc,
        );
        $userToken = $this->auth->getUserToken($username, $password, $expire, $configuration);
        return $userToken['access_token'];
    }


//================================================= 用户体系集成 =========================================================

    /**
     * 开放注册单个用户
     * @param string $username 用户ID为公开信息，请勿使用 UUID、邮箱地址、手机号等敏感信息;请确保同一个app下，用户ID唯一
     * @param string $password 用户的登录密码，长度不可超过 64 个字符
     * @return array
     */
    public function registrationIndividualUsers(string $username, string $password): array
    {
        $data = array(
            'username' => $username,
            'password' => $password,
        );
        return $this->user->create($data);
    }

    /**
     * 开放批量注册用户
     * @param array $data 多个用户Id和密码 [['username'=>用户ID,'password'=>'用户的登录密码']] 单次请求最多可注册 60 个用户 ID。
     * @return array
     */
    public function batchRegistrationIndividualUsers(array $data): array
    {
        return $this->user->create($data);
    }

    /**
     * 获取单个用户的详情
     * @param string $userId
     * @return array
     */
    public function getUserInfo(string $username): array
    {
        return $this->user->get($username);
    }

    /**
     * 批量获取用户
     * @param int $limit 请求查询用户的数量。取值范围为 [1,100]，默认值为 10。若实际用户数量超过 100，返回 100 个用户
     * @param string $cursor 开始获取数据的游标位置，用于分页显示用户列表。第一次发起批量查询用户请求时若不设置 cursor，请求成功后会获得第一页用户列表。从响应 body 中获取 cursor，并在下一次请求的 URL 中传入该 cursor，直到响应 body 中不再有 cursor 字段，则表示已查询到 app 中所有用户。
     * @return array
     */
    public function getBatchUserInfo(int $limit = 10, string $cursor = ""): array
    {
        if ($cursor) {
            return $this->user->listUsers($limit, $cursor);
        }
        return $this->user->listUsers($limit);
    }

    /**
     * 批量获取被封禁的用户
     * @param int $limit 请求查询用户的数量。取值范围为 [1,100]，默认值为 10。若实际用户数量超过 100，返回 100 个用户
     * @param bool $bool
     * @param string $cursor 开始获取数据的游标位置，用于分页显示用户列表。第一次发起批量查询用户请求时若不设置 cursor，请求成功后会获得第一页用户列表。从响应 body 中获取 cursor，并在下一次请求的 URL 中传入该 cursor，直到响应 body 中不再有 cursor 字段，则表示已查询到 app 中所有用户。
     * @return array
     */
    public function getBatchBanUserInfo(int $limit = 10, bool $bool = false, string $cursor = ""): array
    {
        return $this->user->listUsers($limit, $cursor, $bool);
    }

    /**
     * 删除单个用户
     * @param string $username 删除的账号的用户 ID
     * @return array
     */
    public function deleteUser(string $username): array
    {
        return $this->user->delete($username);
    }

    /**
     * 批量删除用户
     * @param int $limit
     * @return array
     */
    public function batchDeleteUser(int $limit): array
    {
        return $this->user->batchDelete($limit);
    }

    /**
     * 修改用户密码
     * @param string $username
     * @param string $password
     * @return array
     */
    public function updatePasswordUser(string $username, string $password): array
    {
        return $this->user->updateUserPassword($username,$password);
    }

    /**
     * 获取用户在线状态
     * @param string $username
     * @return array
     */
    public function getUserOnlineStatus(string $username): array
    {
        return $this->user->isUserOnline($username);
    }


    /**
     * 批量获取用户在线状态
     * @param array $data_array
     * @return array
     */
    public function isUsersOnlineUser(array $data_array): array
    {
        return $this->user->isUsersOnline($data_array);
    }

    /**
     * 用户强制下线
     * @param string $username
     * @return array
     */
    public function forceLogoutAllDevicesUser(string $username): array
    {
        return $this->user->forceLogoutAllDevices($username);
    }


//============================================== 推送设置 ===============================================================

    /**
     * 设置用户推送昵称
     * @param string $username 用户名
     * @param string $nickname 要设置的推送昵称
     * @return array|bool
     */
    public function updateUserNicknamePush(string $username,string $nickname): bool|array
    {
        return $this->push->updateUserNickname($username,$nickname);
    }

    /**
     * 设置推送消息展示方式
     * @param string $username 用户名
     * @param int $notification_display_style 消息提醒方式，0：仅通知；1：通知以及消息详情
     * @return array|bool
     */
    public function setNotificationDisplayStylePush(string $username,int $notification_display_style=1): bool|array
    {
        return $this->push->setNotificationDisplayStyle($username,$notification_display_style);
    }

    /**
     * 开启免打扰，设置免打扰时间
     * @param string $username 用户名
     * @param int $startTime 免打扰起始时间，单位是小时，例如 8 代表每日 8:00 开启免打扰
     * @param int $endTime 免打扰结束时间，单位是小时，例如 18 代表每日 18:00 关闭免打扰
     * @return array|bool
     */
    public function openNotificationNoDisturbingPush(string $username,int $startTime,int $endTime): bool|array
    {
        return $this->push->openNotificationNoDisturbing($username,$startTime,$endTime);
    }

    /**
     * 取消推送免打扰
     * @param string $username 用户名
     * @return array|bool
     */
    public function closeNotificationNoDisturbingPush(string $username): bool|array
    {
        return $this->push->closeNotificationNoDisturbing($username);
    }


//============================================ 消息管理 =================================================================

    /**
     * 获取用户离线消息数
     * @param string $username
     * @return array|int
     */
    public function getCountMissedMessages(string $username): int|array
    {
        return $this->message->countMissedMessages($username);
    }

    /**
     * 发送文本消息
     * @param string $target_type 发送的目标类型: users:给用户发消息，chatgroups:给群发消息，chatrooms:给聊天室发消息
     * @param array $target 发送的目标；注意这里需要用数组，数组内添加的最大用户数默认 600 个，即使只有一个用户，也要用数组 ['u1']；给用户发送时数组元素是用户名，给群组发送时，数组元素是 groupid
     * @param array $ext 扩展
     * @param mixed $message 消息内容
     * @param string $from 表示消息发送者；无此字段 Server 会默认设置为 "from": "admin"，有 from 字段但值为空串 ("") 时请求失
     * @param bool $sync_device 消息发送成功后，是否将消息同步给发送方。true：是；false（默认）：否。
     * @param bool $isOnline 该参数值为 true 时，代表 routetype 的值为 “ROUTE_ONLINE”，表示发送消息时只有接收方在线时，才进行消息投递。若接收方离线，将不会收到此条消息
     * @return array
     */
    public function sendTxtMessage(string $target_type,array $target,mixed $message,string $from,array $ext=[],bool $sync_device=false,bool $isOnline=false): array
    {
        $msg = array(
            'msg' => $message,
            // 扩展，可以没有这个字段，但是如果有，值不能是 “ext:null” 这种形式，否则出错。
            'ext' => $ext,
        );
        return $this->message->text($target_type, $target, $msg, $from,$sync_device, $isOnline);
    }




}
